1   /*
2    * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package javax.management.remote.rmi;
27  
28  import com.sun.jmx.mbeanserver.Util;
29  import com.sun.jmx.remote.internal.ClientCommunicatorAdmin;
30  import com.sun.jmx.remote.internal.ClientListenerInfo;
31  import com.sun.jmx.remote.internal.ClientNotifForwarder;
32  import com.sun.jmx.remote.internal.ProxyRef;
33  import com.sun.jmx.remote.internal.IIOPHelper;
34  import com.sun.jmx.remote.util.ClassLogger;
35  import com.sun.jmx.remote.util.EnvHelp;
36  import java.io.ByteArrayInputStream;
37  import java.io.IOException;
38  import java.io.InputStream;
39  import java.io.InvalidObjectException;
40  import java.io.NotSerializableException;
41  import java.io.ObjectInputStream;
42  import java.io.ObjectStreamClass;
43  import java.io.Serializable;
44  import java.io.WriteAbortedException;
45  import java.lang.ref.WeakReference;
46  import java.lang.reflect.Constructor;
47  import java.lang.reflect.InvocationHandler;
48  import java.lang.reflect.InvocationTargetException;
49  import java.lang.reflect.Proxy;
50  import java.net.MalformedURLException;
51  import java.rmi.MarshalException;
52  import java.rmi.MarshalledObject;
53  import java.rmi.NoSuchObjectException;
54  import java.rmi.Remote;
55  import java.rmi.ServerException;
56  import java.rmi.UnmarshalException;
57  import java.rmi.server.RMIClientSocketFactory;
58  import java.rmi.server.RemoteObject;
59  import java.rmi.server.RemoteObjectInvocationHandler;
60  import java.rmi.server.RemoteRef;
61  import java.security.AccessController;
62  import java.security.PrivilegedAction;
63  import java.security.PrivilegedExceptionAction;
64  import java.security.ProtectionDomain;
65  import java.util.Arrays;
66  import java.util.Collections;
67  import java.util.HashMap;
68  import java.util.Map;
69  import java.util.Properties;
70  import java.util.Set;
71  import java.util.WeakHashMap;
72  import javax.management.Attribute;
73  import javax.management.AttributeList;
74  import javax.management.AttributeNotFoundException;
75  import javax.management.InstanceAlreadyExistsException;
76  import javax.management.InstanceNotFoundException;
77  import javax.management.IntrospectionException;
78  import javax.management.InvalidAttributeValueException;
79  import javax.management.ListenerNotFoundException;
80  import javax.management.MBeanException;
81  import javax.management.MBeanInfo;
82  import javax.management.MBeanRegistrationException;
83  import javax.management.MBeanServerConnection;
84  import javax.management.MBeanServerDelegate;
85  import javax.management.MBeanServerNotification;
86  import javax.management.NotCompliantMBeanException;
87  import javax.management.Notification;
88  import javax.management.NotificationBroadcasterSupport;
89  import javax.management.NotificationFilter;
90  import javax.management.NotificationFilterSupport;
91  import javax.management.NotificationListener;
92  import javax.management.ObjectInstance;
93  import javax.management.ObjectName;
94  import javax.management.QueryExp;
95  import javax.management.ReflectionException;
96  import javax.management.remote.JMXConnectionNotification;
97  import javax.management.remote.JMXConnector;
98  import javax.management.remote.JMXConnectorFactory;
99  import javax.management.remote.JMXServiceURL;
100 import javax.management.remote.NotificationResult;
101 import javax.management.remote.JMXAddressable;
102 import javax.naming.InitialContext;
103 import javax.naming.NamingException;
104 import javax.rmi.ssl.SslRMIClientSocketFactory;
105 import javax.security.auth.Subject;
106 import sun.rmi.server.UnicastRef2;
107 import sun.rmi.transport.LiveRef;
108 
109 /**
110  * <p>A connection to a remote RMI connector.  Usually, such
111  * connections are made using {@link
112  * javax.management.remote.JMXConnectorFactory JMXConnectorFactory}.
113  * However, specialized applications can use this class directly, for
114  * example with an {@link RMIServer} stub obtained without going
115  * through JNDI.</p>
116  *
117  * @since 1.5
118  */
119 public class RMIConnector implements JMXConnector, Serializable, JMXAddressable {
120 
121     private static final ClassLogger logger =
122             new ClassLogger("javax.management.remote.rmi", "RMIConnector");
123 
124     private static final long serialVersionUID = 817323035842634473L;
125 
126     private RMIConnector(RMIServer rmiServer, JMXServiceURL address,
127             Map<String, ?> environment) {
128         if (rmiServer == null && address == null) throw new
129                 IllegalArgumentException("rmiServer and jmxServiceURL both null");
130 
131         initTransients();
132 
133         this.rmiServer = rmiServer;
134         this.jmxServiceURL = address;
135         if (environment == null) {
136             this.env = Collections.emptyMap();
137         } else {
138             EnvHelp.checkAttributes(environment);
139             this.env = Collections.unmodifiableMap(environment);
140         }
141     }
142 
143     /**
144      * <p>Constructs an <code>RMIConnector</code> that will connect
145      * the RMI connector server with the given address.</p>
146      *
147      * <p>The address can refer directly to the connector server,
148      * using one of the following syntaxes:</p>
149      *
150      * <pre>
151      * service:jmx:rmi://<em>[host[:port]]</em>/stub/<em>encoded-stub</em>
152      * service:jmx:iiop://<em>[host[:port]]</em>/ior/<em>encoded-IOR</em>
153      * </pre>
154      *
155      * <p>(Here, the square brackets <code>[]</code> are not part of the
156      * address but indicate that the host and port are optional.)</p>
157      *
158      * <p>The address can instead indicate where to find an RMI stub
159      * through JNDI, using one of the following syntaxes:</p>
160      *
161      * <pre>
162      * service:jmx:rmi://<em>[host[:port]]</em>/jndi/<em>jndi-name</em>
163      * service:jmx:iiop://<em>[host[:port]]</em>/jndi/<em>jndi-name</em>
164      * </pre>
165      *
166      * <p>An implementation may also recognize additional address
167      * syntaxes, for example:</p>
168      *
169      * <pre>
170      * service:jmx:iiop://<em>[host[:port]]</em>/stub/<em>encoded-stub</em>
171      * </pre>
172      *
173      * @param url the address of the RMI connector server.
174      *
175      * @param environment additional attributes specifying how to make
176      * the connection.  For JNDI-based addresses, these attributes can
177      * usefully include JNDI attributes recognized by {@link
178      * InitialContext#InitialContext(Hashtable) InitialContext}.  This
179      * parameter can be null, which is equivalent to an empty Map.
180      *
181      * @exception IllegalArgumentException if <code>url</code>
182      * is null.
183      */
184     public RMIConnector(JMXServiceURL url, Map<String,?> environment) {
185         this(null, url, environment);
186     }
187 
188     /**
189      * <p>Constructs an <code>RMIConnector</code> using the given RMI stub.
190      *
191      * @param rmiServer an RMI stub representing the RMI connector server.
192      * @param environment additional attributes specifying how to make
193      * the connection.  This parameter can be null, which is
194      * equivalent to an empty Map.
195      *
196      * @exception IllegalArgumentException if <code>rmiServer</code>
197      * is null.
198      */
199     public RMIConnector(RMIServer rmiServer, Map<String,?> environment) {
200         this(rmiServer, null, environment);
201     }
202 
203     /**
204      * <p>Returns a string representation of this object.  In general,
205      * the <code>toString</code> method returns a string that
206      * "textually represents" this object. The result should be a
207      * concise but informative representation that is easy for a
208      * person to read.</p>
209      *
210      * @return a String representation of this object.
211      **/
212     @Override
213     public String toString() {
214         final StringBuilder b = new StringBuilder(this.getClass().getName());
215         b.append(":");
216         if (rmiServer != null) {
217             b.append(" rmiServer=").append(rmiServer.toString());
218         }
219         if (jmxServiceURL != null) {
220             if (rmiServer!=null) b.append(",");
221             b.append(" jmxServiceURL=").append(jmxServiceURL.toString());
222         }
223         return b.toString();
224     }
225 
226     /**
227      * <p>The address of this connector.</p>
228      *
229      * @return the address of this connector, or null if it
230      * does not have one.
231      *
232      * @since 1.6
233      */
234     public JMXServiceURL getAddress() {
235         return jmxServiceURL;
236     }
237 
238     //--------------------------------------------------------------------
239     // implements JMXConnector interface
240     //--------------------------------------------------------------------
241     public void connect() throws IOException {
242         connect(null);
243     }
244 
245     public synchronized void connect(Map<String,?> environment)
246     throws IOException {
247         final boolean tracing = logger.traceOn();
248         String        idstr   = (tracing?"["+this.toString()+"]":null);
249 
250         if (terminated) {
251             logger.trace("connect",idstr + " already closed.");
252             throw new IOException("Connector closed");
253         }
254         if (connected) {
255             logger.trace("connect",idstr + " already connected.");
256             return;
257         }
258 
259         try {
260             if (tracing) logger.trace("connect",idstr + " connecting...");
261 
262             final Map<String, Object> usemap =
263                     new HashMap<String, Object>((this.env==null) ?
264                         Collections.<String, Object>emptyMap() : this.env);
265 
266 
267             if (environment != null) {
268                 EnvHelp.checkAttributes(environment);
269                 usemap.putAll(environment);
270             }
271 
272             // Get RMIServer stub from directory or URL encoding if needed.
273             if (tracing) logger.trace("connect",idstr + " finding stub...");
274             RMIServer stub = (rmiServer!=null)?rmiServer:
275                 findRMIServer(jmxServiceURL, usemap);
276 
277             // Check for secure RMIServer stub if the corresponding
278             // client-side environment property is set to "true".
279             //
280             boolean checkStub = EnvHelp.computeBooleanFromString(
281                     usemap,
282                     "jmx.remote.x.check.stub",false);
283             if (checkStub) checkStub(stub, rmiServerImplStubClass);
284 
285             // Connect IIOP Stub if needed.
286             if (tracing) logger.trace("connect",idstr + " connecting stub...");
287             stub = connectStub(stub,usemap);
288             idstr = (tracing?"["+this.toString()+"]":null);
289 
290             // Calling newClient on the RMIServer stub.
291             if (tracing)
292                 logger.trace("connect",idstr + " getting connection...");
293             Object credentials = usemap.get(CREDENTIALS);
294 
295             try {
296                 connection = getConnection(stub, credentials, checkStub);
297             } catch (java.rmi.RemoteException re) {
298                 if (jmxServiceURL != null) {
299                     final String pro = jmxServiceURL.getProtocol();
300                     final String path = jmxServiceURL.getURLPath();
301 
302                     if ("rmi".equals(pro) &&
303                         path.startsWith("/jndi/iiop:")) {
304                         MalformedURLException mfe = new MalformedURLException(
305                               "Protocol is rmi but JNDI scheme is iiop: " + jmxServiceURL);
306                         mfe.initCause(re);
307                         throw mfe;
308                     }
309                 }
310                 throw re;
311             }
312 
313             // Always use one of:
314             //   ClassLoader provided in Map at connect time,
315             //   or contextClassLoader at connect time.
316             if (tracing)
317                 logger.trace("connect",idstr + " getting class loader...");
318             defaultClassLoader = EnvHelp.resolveClientClassLoader(usemap);
319 
320             usemap.put(JMXConnectorFactory.DEFAULT_CLASS_LOADER,
321                     defaultClassLoader);
322 
323             rmiNotifClient = new RMINotifClient(defaultClassLoader, usemap);
324 
325             env = usemap;
326             final long checkPeriod = EnvHelp.getConnectionCheckPeriod(usemap);
327             communicatorAdmin = new RMIClientCommunicatorAdmin(checkPeriod);
328 
329             connected = true;
330 
331             // The connectionId variable is used in doStart(), when
332             // reconnecting, to identify the "old" connection.
333             //
334             connectionId = getConnectionId();
335 
336             Notification connectedNotif =
337                     new JMXConnectionNotification(JMXConnectionNotification.OPENED,
338                     this,
339                     connectionId,
340                     clientNotifSeqNo++,
341                     "Successful connection",
342                     null);
343             sendNotification(connectedNotif);
344 
345             if (tracing) logger.trace("connect",idstr + " done...");
346         } catch (IOException e) {
347             if (tracing)
348                 logger.trace("connect",idstr + " failed to connect: " + e);
349             throw e;
350         } catch (RuntimeException e) {
351             if (tracing)
352                 logger.trace("connect",idstr + " failed to connect: " + e);
353             throw e;
354         } catch (NamingException e) {
355             final String msg = "Failed to retrieve RMIServer stub: " + e;
356             if (tracing) logger.trace("connect",idstr + " " + msg);
357             throw EnvHelp.initCause(new IOException(msg),e);
358         }
359     }
360 
361     public synchronized String getConnectionId() throws IOException {
362         if (terminated || !connected) {
363             if (logger.traceOn())
364                 logger.trace("getConnectionId","["+this.toString()+
365                         "] not connected.");
366 
367             throw new IOException("Not connected");
368         }
369 
370         // we do a remote call to have an IOException if the connection is broken.
371         // see the bug 4939578
372         return connection.getConnectionId();
373     }
374 
375     public synchronized MBeanServerConnection getMBeanServerConnection()
376     throws IOException {
377         return getMBeanServerConnection(null);
378     }
379 
380     public synchronized MBeanServerConnection
381             getMBeanServerConnection(Subject delegationSubject)
382             throws IOException {
383 
384         if (terminated) {
385             if (logger.traceOn())
386                 logger.trace("getMBeanServerConnection","[" + this.toString() +
387                         "] already closed.");
388             throw new IOException("Connection closed");
389         } else if (!connected) {
390             if (logger.traceOn())
391                 logger.trace("getMBeanServerConnection","[" + this.toString() +
392                         "] is not connected.");
393             throw new IOException("Not connected");
394         }
395 
396         MBeanServerConnection rmbsc = rmbscMap.get(delegationSubject);
397         if (rmbsc != null) {
398             return rmbsc;
399         }
400 
401         rmbsc = new RemoteMBeanServerConnection(delegationSubject);
402         rmbscMap.put(delegationSubject, rmbsc);
403         return rmbsc;
404     }
405 
406     public void
407             addConnectionNotificationListener(NotificationListener listener,
408             NotificationFilter filter,
409             Object handback) {
410         if (listener == null)
411             throw new NullPointerException("listener");
412         connectionBroadcaster.addNotificationListener(listener, filter,
413                 handback);
414     }
415 
416     public void
417             removeConnectionNotificationListener(NotificationListener listener)
418             throws ListenerNotFoundException {
419         if (listener == null)
420             throw new NullPointerException("listener");
421         connectionBroadcaster.removeNotificationListener(listener);
422     }
423 
424     public void
425             removeConnectionNotificationListener(NotificationListener listener,
426             NotificationFilter filter,
427             Object handback)
428             throws ListenerNotFoundException {
429         if (listener == null)
430             throw new NullPointerException("listener");
431         connectionBroadcaster.removeNotificationListener(listener, filter,
432                 handback);
433     }
434 
435     private void sendNotification(Notification n) {
436         connectionBroadcaster.sendNotification(n);
437     }
438 
439     public synchronized void close() throws IOException {
440         close(false);
441     }
442 
443     // allows to do close after setting the flag "terminated" to true.
444     // It is necessary to avoid a deadlock, see 6296324
445     private synchronized void close(boolean intern) throws IOException {
446         final boolean tracing = logger.traceOn();
447         final boolean debug   = logger.debugOn();
448         final String  idstr   = (tracing?"["+this.toString()+"]":null);
449 
450         if (!intern) {
451             // Return if already cleanly closed.
452             //
453             if (terminated) {
454                 if (closeException == null) {
455                     if (tracing) logger.trace("close",idstr + " already closed.");
456                     return;
457                 }
458             } else {
459                 terminated = true;
460             }
461         }
462 
463         if (closeException != null && tracing) {
464             // Already closed, but not cleanly. Attempt again.
465             //
466             if (tracing) {
467                 logger.trace("close",idstr + " had failed: " + closeException);
468                 logger.trace("close",idstr + " attempting to close again.");
469             }
470         }
471 
472         String savedConnectionId = null;
473         if (connected) {
474             savedConnectionId = connectionId;
475         }
476 
477         closeException = null;
478 
479         if (tracing) logger.trace("close",idstr + " closing.");
480 
481         if (communicatorAdmin != null) {
482             communicatorAdmin.terminate();
483         }
484 
485         if (rmiNotifClient != null) {
486             try {
487                 rmiNotifClient.terminate();
488                 if (tracing) logger.trace("close",idstr +
489                         " RMI Notification client terminated.");
490             } catch (RuntimeException x) {
491                 closeException = x;
492                 if (tracing) logger.trace("close",idstr +
493                         " Failed to terminate RMI Notification client: " + x);
494                 if (debug) logger.debug("close",x);
495             }
496         }
497 
498         if (connection != null) {
499             try {
500                 connection.close();
501                 if (tracing) logger.trace("close",idstr + " closed.");
502             } catch (NoSuchObjectException nse) {
503                 // OK, the server maybe closed itself.
504             } catch (IOException e) {
505                 closeException = e;
506                 if (tracing) logger.trace("close",idstr +
507                         " Failed to close RMIServer: " + e);
508                 if (debug) logger.debug("close",e);
509             }
510         }
511 
512         // Clean up MBeanServerConnection table
513         //
514         rmbscMap.clear();
515 
516         /* Send notification of closure.  We don't do this if the user
517          * never called connect() on the connector, because there's no
518          * connection id in that case.  */
519 
520         if (savedConnectionId != null) {
521             Notification closedNotif =
522                     new JMXConnectionNotification(JMXConnectionNotification.CLOSED,
523                     this,
524                     savedConnectionId,
525                     clientNotifSeqNo++,
526                     "Client has been closed",
527                     null);
528             sendNotification(closedNotif);
529         }
530 
531         // throw exception if needed
532         //
533         if (closeException != null) {
534             if (tracing) logger.trace("close",idstr + " failed to close: " +
535                     closeException);
536             if (closeException instanceof IOException)
537                 throw (IOException) closeException;
538             if (closeException instanceof RuntimeException)
539                 throw (RuntimeException) closeException;
540             final IOException x =
541                     new IOException("Failed to close: " + closeException);
542             throw EnvHelp.initCause(x,closeException);
543         }
544     }
545 
546     // added for re-connection
547     private Integer addListenerWithSubject(ObjectName name,
548                                            MarshalledObject<NotificationFilter> filter,
549                                            Subject delegationSubject,
550                                            boolean reconnect)
551         throws InstanceNotFoundException, IOException {
552 
553         final boolean debug = logger.debugOn();
554         if (debug)
555             logger.debug("addListenerWithSubject",
556                     "(ObjectName,MarshalledObject,Subject)");
557 
558         final ObjectName[] names = new ObjectName[] {name};
559         final MarshalledObject<NotificationFilter>[] filters =
560                 Util.cast(new MarshalledObject<?>[] {filter});
561         final Subject[] delegationSubjects = new Subject[] {
562             delegationSubject
563         };
564 
565         final Integer[] listenerIDs =
566                 addListenersWithSubjects(names,filters,delegationSubjects,
567                 reconnect);
568 
569         if (debug) logger.debug("addListenerWithSubject","listenerID="
570                 + listenerIDs[0]);
571         return listenerIDs[0];
572     }
573 
574     // added for re-connection
575     private Integer[] addListenersWithSubjects(ObjectName[]       names,
576                              MarshalledObject<NotificationFilter>[] filters,
577                              Subject[]          delegationSubjects,
578                              boolean            reconnect)
579         throws InstanceNotFoundException, IOException {
580 
581         final boolean debug = logger.debugOn();
582         if (debug)
583             logger.debug("addListenersWithSubjects",
584                     "(ObjectName[],MarshalledObject[],Subject[])");
585 
586         final ClassLoader old = pushDefaultClassLoader();
587         Integer[] listenerIDs = null;
588 
589         try {
590             listenerIDs = connection.addNotificationListeners(names,
591                     filters,
592                     delegationSubjects);
593         } catch (NoSuchObjectException noe) {
594             // maybe reconnect
595             if (reconnect) {
596                 communicatorAdmin.gotIOException(noe);
597 
598                 listenerIDs = connection.addNotificationListeners(names,
599                         filters,
600                         delegationSubjects);
601             } else {
602                 throw noe;
603             }
604         } catch (IOException ioe) {
605             // send a failed notif if necessary
606             communicatorAdmin.gotIOException(ioe);
607         } finally {
608             popDefaultClassLoader(old);
609         }
610 
611         if (debug) logger.debug("addListenersWithSubjects","registered "
612                 + ((listenerIDs==null)?0:listenerIDs.length)
613                 + " listener(s)");
614         return listenerIDs;
615     }
616 
617     //--------------------------------------------------------------------
618     // Implementation of MBeanServerConnection
619     //--------------------------------------------------------------------
620     private class RemoteMBeanServerConnection implements MBeanServerConnection {
621         private Subject delegationSubject;
622 
623         public RemoteMBeanServerConnection() {
624             this(null);
625         }
626 
627         public RemoteMBeanServerConnection(Subject delegationSubject) {
628             this.delegationSubject = delegationSubject;
629         }
630 
631         public ObjectInstance createMBean(String className,
632                 ObjectName name)
633                 throws ReflectionException,
634                 InstanceAlreadyExistsException,
635                 MBeanRegistrationException,
636                 MBeanException,
637                 NotCompliantMBeanException,
638                 IOException {
639             if (logger.debugOn())
640                 logger.debug("createMBean(String,ObjectName)",
641                         "className=" + className + ", name=" +
642                         name);
643 
644             final ClassLoader old = pushDefaultClassLoader();
645             try {
646                 return connection.createMBean(className,
647                         name,
648                         delegationSubject);
649             } catch (IOException ioe) {
650                 communicatorAdmin.gotIOException(ioe);
651 
652                 return connection.createMBean(className,
653                         name,
654                         delegationSubject);
655             } finally {
656                 popDefaultClassLoader(old);
657             }
658         }
659 
660         public ObjectInstance createMBean(String className,
661                 ObjectName name,
662                 ObjectName loaderName)
663                 throws ReflectionException,
664                 InstanceAlreadyExistsException,
665                 MBeanRegistrationException,
666                 MBeanException,
667                 NotCompliantMBeanException,
668                 InstanceNotFoundException,
669                 IOException {
670 
671             if (logger.debugOn())
672                 logger.debug("createMBean(String,ObjectName,ObjectName)",
673                         "className=" + className + ", name="
674                         + name + ", loaderName="
675                         + loaderName + ")");
676 
677             final ClassLoader old = pushDefaultClassLoader();
678             try {
679                 return connection.createMBean(className,
680                         name,
681                         loaderName,
682                         delegationSubject);
683 
684             } catch (IOException ioe) {
685                 communicatorAdmin.gotIOException(ioe);
686 
687                 return connection.createMBean(className,
688                         name,
689                         loaderName,
690                         delegationSubject);
691 
692             } finally {
693                 popDefaultClassLoader(old);
694             }
695         }
696 
697         public ObjectInstance createMBean(String className,
698                 ObjectName name,
699                 Object params[],
700                 String signature[])
701                 throws ReflectionException,
702                 InstanceAlreadyExistsException,
703                 MBeanRegistrationException,
704                 MBeanException,
705                 NotCompliantMBeanException,
706                 IOException {
707             if (logger.debugOn())
708                 logger.debug("createMBean(String,ObjectName,Object[],String[])",
709                         "className=" + className + ", name="
710                         + name + ", params="
711                         + objects(params) + ", signature="
712                         + strings(signature));
713 
714             final MarshalledObject<Object[]> sParams =
715                     new MarshalledObject<Object[]>(params);
716             final ClassLoader old = pushDefaultClassLoader();
717             try {
718                 return connection.createMBean(className,
719                         name,
720                         sParams,
721                         signature,
722                         delegationSubject);
723             } catch (IOException ioe) {
724                 communicatorAdmin.gotIOException(ioe);
725 
726                 return connection.createMBean(className,
727                         name,
728                         sParams,
729                         signature,
730                         delegationSubject);
731             } finally {
732                 popDefaultClassLoader(old);
733             }
734         }
735 
736         public ObjectInstance createMBean(String className,
737                 ObjectName name,
738                 ObjectName loaderName,
739                 Object params[],
740                 String signature[])
741                 throws ReflectionException,
742                 InstanceAlreadyExistsException,
743                 MBeanRegistrationException,
744                 MBeanException,
745                 NotCompliantMBeanException,
746                 InstanceNotFoundException,
747                 IOException {
748             if (logger.debugOn()) logger.debug(
749                     "createMBean(String,ObjectName,ObjectName,Object[],String[])",
750                     "className=" + className + ", name=" + name + ", loaderName="
751                     + loaderName + ", params=" + objects(params)
752                     + ", signature=" + strings(signature));
753 
754             final MarshalledObject<Object[]> sParams =
755                     new MarshalledObject<Object[]>(params);
756             final ClassLoader old = pushDefaultClassLoader();
757             try {
758                 return connection.createMBean(className,
759                         name,
760                         loaderName,
761                         sParams,
762                         signature,
763                         delegationSubject);
764             } catch (IOException ioe) {
765                 communicatorAdmin.gotIOException(ioe);
766 
767                 return connection.createMBean(className,
768                         name,
769                         loaderName,
770                         sParams,
771                         signature,
772                         delegationSubject);
773             } finally {
774                 popDefaultClassLoader(old);
775             }
776         }
777 
778         public void unregisterMBean(ObjectName name)
779         throws InstanceNotFoundException,
780                 MBeanRegistrationException,
781                 IOException {
782             if (logger.debugOn())
783                 logger.debug("unregisterMBean", "name=" + name);
784 
785             final ClassLoader old = pushDefaultClassLoader();
786             try {
787                 connection.unregisterMBean(name, delegationSubject);
788             } catch (IOException ioe) {
789                 communicatorAdmin.gotIOException(ioe);
790 
791                 connection.unregisterMBean(name, delegationSubject);
792             } finally {
793                 popDefaultClassLoader(old);
794             }
795         }
796 
797         public ObjectInstance getObjectInstance(ObjectName name)
798         throws InstanceNotFoundException,
799                 IOException {
800             if (logger.debugOn())
801                 logger.debug("getObjectInstance", "name=" + name);
802 
803             final ClassLoader old = pushDefaultClassLoader();
804             try {
805                 return connection.getObjectInstance(name, delegationSubject);
806             } catch (IOException ioe) {
807                 communicatorAdmin.gotIOException(ioe);
808 
809                 return connection.getObjectInstance(name, delegationSubject);
810             } finally {
811                 popDefaultClassLoader(old);
812             }
813         }
814 
815         public Set<ObjectInstance> queryMBeans(ObjectName name,
816                 QueryExp query)
817                 throws IOException {
818             if (logger.debugOn()) logger.debug("queryMBeans",
819                     "name=" + name + ", query=" + query);
820 
821             final MarshalledObject<QueryExp> sQuery =
822                     new MarshalledObject<QueryExp>(query);
823             final ClassLoader old = pushDefaultClassLoader();
824             try {
825                 return connection.queryMBeans(name, sQuery, delegationSubject);
826             } catch (IOException ioe) {
827                 communicatorAdmin.gotIOException(ioe);
828 
829                 return connection.queryMBeans(name, sQuery, delegationSubject);
830             } finally {
831                 popDefaultClassLoader(old);
832             }
833         }
834 
835         public Set<ObjectName> queryNames(ObjectName name,
836                 QueryExp query)
837                 throws IOException {
838             if (logger.debugOn()) logger.debug("queryNames",
839                     "name=" + name + ", query=" + query);
840 
841             final MarshalledObject<QueryExp> sQuery =
842                     new MarshalledObject<QueryExp>(query);
843             final ClassLoader old = pushDefaultClassLoader();
844             try {
845                 return connection.queryNames(name, sQuery, delegationSubject);
846             } catch (IOException ioe) {
847                 communicatorAdmin.gotIOException(ioe);
848 
849                 return connection.queryNames(name, sQuery, delegationSubject);
850             } finally {
851                 popDefaultClassLoader(old);
852             }
853         }
854 
855         public boolean isRegistered(ObjectName name)
856         throws IOException {
857             if (logger.debugOn())
858                 logger.debug("isRegistered", "name=" + name);
859 
860             final ClassLoader old = pushDefaultClassLoader();
861             try {
862                 return connection.isRegistered(name, delegationSubject);
863             } catch (IOException ioe) {
864                 communicatorAdmin.gotIOException(ioe);
865 
866                 return connection.isRegistered(name, delegationSubject);
867             } finally {
868                 popDefaultClassLoader(old);
869             }
870         }
871 
872         public Integer getMBeanCount()
873         throws IOException {
874             if (logger.debugOn()) logger.debug("getMBeanCount", "");
875 
876             final ClassLoader old = pushDefaultClassLoader();
877             try {
878                 return connection.getMBeanCount(delegationSubject);
879             } catch (IOException ioe) {
880                 communicatorAdmin.gotIOException(ioe);
881 
882                 return connection.getMBeanCount(delegationSubject);
883             } finally {
884                 popDefaultClassLoader(old);
885             }
886         }
887 
888         public Object getAttribute(ObjectName name,
889                 String attribute)
890                 throws MBeanException,
891                 AttributeNotFoundException,
892                 InstanceNotFoundException,
893                 ReflectionException,
894                 IOException {
895             if (logger.debugOn()) logger.debug("getAttribute",
896                     "name=" + name + ", attribute="
897                     + attribute);
898 
899             final ClassLoader old = pushDefaultClassLoader();
900             try {
901                 return connection.getAttribute(name,
902                         attribute,
903                         delegationSubject);
904             } catch (IOException ioe) {
905                 communicatorAdmin.gotIOException(ioe);
906 
907                 return connection.getAttribute(name,
908                         attribute,
909                         delegationSubject);
910             } finally {
911                 popDefaultClassLoader(old);
912             }
913         }
914 
915         public AttributeList getAttributes(ObjectName name,
916                 String[] attributes)
917                 throws InstanceNotFoundException,
918                 ReflectionException,
919                 IOException {
920             if (logger.debugOn()) logger.debug("getAttributes",
921                     "name=" + name + ", attributes="
922                     + strings(attributes));
923 
924             final ClassLoader old = pushDefaultClassLoader();
925             try {
926                 return connection.getAttributes(name,
927                         attributes,
928                         delegationSubject);
929 
930             } catch (IOException ioe) {
931                 communicatorAdmin.gotIOException(ioe);
932 
933                 return connection.getAttributes(name,
934                         attributes,
935                         delegationSubject);
936             } finally {
937                 popDefaultClassLoader(old);
938             }
939         }
940 
941 
942         public void setAttribute(ObjectName name,
943                 Attribute attribute)
944                 throws InstanceNotFoundException,
945                 AttributeNotFoundException,
946                 InvalidAttributeValueException,
947                 MBeanException,
948                 ReflectionException,
949                 IOException {
950 
951             if (logger.debugOn()) logger.debug("setAttribute",
952                     "name=" + name + ", attribute="
953                     + attribute);
954 
955             final MarshalledObject<Attribute> sAttribute =
956                     new MarshalledObject<Attribute>(attribute);
957             final ClassLoader old = pushDefaultClassLoader();
958             try {
959                 connection.setAttribute(name, sAttribute, delegationSubject);
960             } catch (IOException ioe) {
961                 communicatorAdmin.gotIOException(ioe);
962 
963                 connection.setAttribute(name, sAttribute, delegationSubject);
964             } finally {
965                 popDefaultClassLoader(old);
966             }
967         }
968 
969         public AttributeList setAttributes(ObjectName name,
970                 AttributeList attributes)
971                 throws InstanceNotFoundException,
972                 ReflectionException,
973                 IOException {
974 
975             if (logger.debugOn()) logger.debug("setAttributes",
976                     "name=" + name + ", attributes="
977                     + attributes);
978 
979             final MarshalledObject<AttributeList> sAttributes =
980                     new MarshalledObject<AttributeList>(attributes);
981             final ClassLoader old = pushDefaultClassLoader();
982             try {
983                 return connection.setAttributes(name,
984                         sAttributes,
985                         delegationSubject);
986             } catch (IOException ioe) {
987                 communicatorAdmin.gotIOException(ioe);
988 
989                 return connection.setAttributes(name,
990                         sAttributes,
991                         delegationSubject);
992             } finally {
993                 popDefaultClassLoader(old);
994             }
995         }
996 
997 
998         public Object invoke(ObjectName name,
999                 String operationName,
1000                 Object params[],
1001                 String signature[])
1002                 throws InstanceNotFoundException,
1003                 MBeanException,
1004                 ReflectionException,
1005                 IOException {
1006 
1007             if (logger.debugOn()) logger.debug("invoke",
1008                     "name=" + name
1009                     + ", operationName=" + operationName
1010                     + ", params=" + objects(params)
1011                     + ", signature=" + strings(signature));
1012 
1013             final MarshalledObject<Object[]> sParams =
1014                     new MarshalledObject<Object[]>(params);
1015             final ClassLoader old = pushDefaultClassLoader();
1016             try {
1017                 return connection.invoke(name,
1018                         operationName,
1019                         sParams,
1020                         signature,
1021                         delegationSubject);
1022             } catch (IOException ioe) {
1023                 communicatorAdmin.gotIOException(ioe);
1024 
1025                 return connection.invoke(name,
1026                         operationName,
1027                         sParams,
1028                         signature,
1029                         delegationSubject);
1030             } finally {
1031                 popDefaultClassLoader(old);
1032             }
1033         }
1034 
1035 
1036         public String getDefaultDomain()
1037         throws IOException {
1038             if (logger.debugOn()) logger.debug("getDefaultDomain", "");
1039 
1040             final ClassLoader old = pushDefaultClassLoader();
1041             try {
1042                 return connection.getDefaultDomain(delegationSubject);
1043             } catch (IOException ioe) {
1044                 communicatorAdmin.gotIOException(ioe);
1045 
1046                 return connection.getDefaultDomain(delegationSubject);
1047             } finally {
1048                 popDefaultClassLoader(old);
1049             }
1050         }
1051 
1052         public String[] getDomains() throws IOException {
1053             if (logger.debugOn()) logger.debug("getDomains", "");
1054 
1055             final ClassLoader old = pushDefaultClassLoader();
1056             try {
1057                 return connection.getDomains(delegationSubject);
1058             } catch (IOException ioe) {
1059                 communicatorAdmin.gotIOException(ioe);
1060 
1061                 return connection.getDomains(delegationSubject);
1062             } finally {
1063                 popDefaultClassLoader(old);
1064             }
1065         }
1066 
1067         public MBeanInfo getMBeanInfo(ObjectName name)
1068         throws InstanceNotFoundException,
1069                 IntrospectionException,
1070                 ReflectionException,
1071                 IOException {
1072 
1073             if (logger.debugOn()) logger.debug("getMBeanInfo", "name=" + name);
1074             final ClassLoader old = pushDefaultClassLoader();
1075             try {
1076                 return connection.getMBeanInfo(name, delegationSubject);
1077             } catch (IOException ioe) {
1078                 communicatorAdmin.gotIOException(ioe);
1079 
1080                 return connection.getMBeanInfo(name, delegationSubject);
1081             } finally {
1082                 popDefaultClassLoader(old);
1083             }
1084         }
1085 
1086 
1087         public boolean isInstanceOf(ObjectName name,
1088                 String className)
1089                 throws InstanceNotFoundException,
1090                 IOException {
1091             if (logger.debugOn())
1092                 logger.debug("isInstanceOf", "name=" + name +
1093                         ", className=" + className);
1094 
1095             final ClassLoader old = pushDefaultClassLoader();
1096             try {
1097                 return connection.isInstanceOf(name,
1098                         className,
1099                         delegationSubject);
1100             } catch (IOException ioe) {
1101                 communicatorAdmin.gotIOException(ioe);
1102 
1103                 return connection.isInstanceOf(name,
1104                         className,
1105                         delegationSubject);
1106             } finally {
1107                 popDefaultClassLoader(old);
1108             }
1109         }
1110 
1111         public void addNotificationListener(ObjectName name,
1112                 ObjectName listener,
1113                 NotificationFilter filter,
1114                 Object handback)
1115                 throws InstanceNotFoundException,
1116                 IOException {
1117 
1118             if (logger.debugOn())
1119                 logger.debug("addNotificationListener" +
1120                         "(ObjectName,ObjectName,NotificationFilter,Object)",
1121                         "name=" + name + ", listener=" + listener
1122                         + ", filter=" + filter + ", handback=" + handback);
1123 
1124             final MarshalledObject<NotificationFilter> sFilter =
1125                     new MarshalledObject<NotificationFilter>(filter);
1126             final MarshalledObject<Object> sHandback =
1127                     new MarshalledObject<Object>(handback);
1128             final ClassLoader old = pushDefaultClassLoader();
1129             try {
1130                 connection.addNotificationListener(name,
1131                         listener,
1132                         sFilter,
1133                         sHandback,
1134                         delegationSubject);
1135             } catch (IOException ioe) {
1136                 communicatorAdmin.gotIOException(ioe);
1137 
1138                 connection.addNotificationListener(name,
1139                         listener,
1140                         sFilter,
1141                         sHandback,
1142                         delegationSubject);
1143             } finally {
1144                 popDefaultClassLoader(old);
1145             }
1146         }
1147 
1148         public void removeNotificationListener(ObjectName name,
1149                 ObjectName listener)
1150                 throws InstanceNotFoundException,
1151                 ListenerNotFoundException,
1152                 IOException {
1153 
1154             if (logger.debugOn()) logger.debug("removeNotificationListener" +
1155                     "(ObjectName,ObjectName)",
1156                     "name=" + name
1157                     + ", listener=" + listener);
1158 
1159             final ClassLoader old = pushDefaultClassLoader();
1160             try {
1161                 connection.removeNotificationListener(name,
1162                         listener,
1163                         delegationSubject);
1164             } catch (IOException ioe) {
1165                 communicatorAdmin.gotIOException(ioe);
1166 
1167                 connection.removeNotificationListener(name,
1168                         listener,
1169                         delegationSubject);
1170             } finally {
1171                 popDefaultClassLoader(old);
1172             }
1173         }
1174 
1175         public void removeNotificationListener(ObjectName name,
1176                 ObjectName listener,
1177                 NotificationFilter filter,
1178                 Object handback)
1179                 throws InstanceNotFoundException,
1180                 ListenerNotFoundException,
1181                 IOException {
1182             if (logger.debugOn())
1183                 logger.debug("removeNotificationListener" +
1184                         "(ObjectName,ObjectName,NotificationFilter,Object)",
1185                         "name=" + name
1186                         + ", listener=" + listener
1187                         + ", filter=" + filter
1188                         + ", handback=" + handback);
1189 
1190             final MarshalledObject<NotificationFilter> sFilter =
1191                     new MarshalledObject<NotificationFilter>(filter);
1192             final MarshalledObject<Object> sHandback =
1193                     new MarshalledObject<Object>(handback);
1194             final ClassLoader old = pushDefaultClassLoader();
1195             try {
1196                 connection.removeNotificationListener(name,
1197                         listener,
1198                         sFilter,
1199                         sHandback,
1200                         delegationSubject);
1201             } catch (IOException ioe) {
1202                 communicatorAdmin.gotIOException(ioe);
1203 
1204                 connection.removeNotificationListener(name,
1205                         listener,
1206                         sFilter,
1207                         sHandback,
1208                         delegationSubject);
1209             } finally {
1210                 popDefaultClassLoader(old);
1211             }
1212         }
1213 
1214         // Specific Notification Handle ----------------------------------
1215 
1216         public void addNotificationListener(ObjectName name,
1217                 NotificationListener listener,
1218                 NotificationFilter filter,
1219                 Object handback)
1220                 throws InstanceNotFoundException,
1221                 IOException {
1222 
1223             final boolean debug = logger.debugOn();
1224 
1225             if (debug)
1226                 logger.debug("addNotificationListener" +
1227                         "(ObjectName,NotificationListener,"+
1228                         "NotificationFilter,Object)",
1229                         "name=" + name
1230                         + ", listener=" + listener
1231                         + ", filter=" + filter
1232                         + ", handback=" + handback);
1233 
1234             final Integer listenerID =
1235                     addListenerWithSubject(name,
1236                     new MarshalledObject<NotificationFilter>(filter),
1237                     delegationSubject,true);
1238             rmiNotifClient.addNotificationListener(listenerID, name, listener,
1239                     filter, handback,
1240                     delegationSubject);
1241         }
1242 
1243         public void removeNotificationListener(ObjectName name,
1244                 NotificationListener listener)
1245                 throws InstanceNotFoundException,
1246                 ListenerNotFoundException,
1247                 IOException {
1248 
1249             final boolean debug = logger.debugOn();
1250 
1251             if (debug) logger.debug("removeNotificationListener"+
1252                     "(ObjectName,NotificationListener)",
1253                     "name=" + name
1254                     + ", listener=" + listener);
1255 
1256             final Integer[] ret =
1257                     rmiNotifClient.removeNotificationListener(name, listener);
1258 
1259             if (debug) logger.debug("removeNotificationListener",
1260                     "listenerIDs=" + objects(ret));
1261 
1262             final ClassLoader old = pushDefaultClassLoader();
1263 
1264             try {
1265                 connection.removeNotificationListeners(name,
1266                         ret,
1267                         delegationSubject);
1268             } catch (IOException ioe) {
1269                 communicatorAdmin.gotIOException(ioe);
1270 
1271                 connection.removeNotificationListeners(name,
1272                         ret,
1273                         delegationSubject);
1274             } finally {
1275                 popDefaultClassLoader(old);
1276             }
1277 
1278         }
1279 
1280         public void removeNotificationListener(ObjectName name,
1281                 NotificationListener listener,
1282                 NotificationFilter filter,
1283                 Object handback)
1284                 throws InstanceNotFoundException,
1285                 ListenerNotFoundException,
1286                 IOException {
1287             final boolean debug = logger.debugOn();
1288 
1289             if (debug)
1290                 logger.debug("removeNotificationListener"+
1291                         "(ObjectName,NotificationListener,"+
1292                         "NotificationFilter,Object)",
1293                         "name=" + name
1294                         + ", listener=" + listener
1295                         + ", filter=" + filter
1296                         + ", handback=" + handback);
1297 
1298             final Integer ret =
1299                     rmiNotifClient.removeNotificationListener(name, listener,
1300                     filter, handback);
1301 
1302             if (debug) logger.debug("removeNotificationListener",
1303                     "listenerID=" + ret);
1304 
1305             final ClassLoader old = pushDefaultClassLoader();
1306             try {
1307                 connection.removeNotificationListeners(name,
1308                         new Integer[] {ret},
1309                         delegationSubject);
1310             } catch (IOException ioe) {
1311                 communicatorAdmin.gotIOException(ioe);
1312 
1313                 connection.removeNotificationListeners(name,
1314                         new Integer[] {ret},
1315                         delegationSubject);
1316             } finally {
1317                 popDefaultClassLoader(old);
1318             }
1319 
1320         }
1321     }
1322 
1323     //--------------------------------------------------------------------
1324     private class RMINotifClient extends ClientNotifForwarder {
1325         public RMINotifClient(ClassLoader cl, Map<String, ?> env) {
1326             super(cl, env);
1327         }
1328 
1329         protected NotificationResult fetchNotifs(long clientSequenceNumber,
1330                 int maxNotifications,
1331                 long timeout)
1332                 throws IOException, ClassNotFoundException {
1333             IOException org;
1334 
1335             while (true) { // used for a successful re-connection
1336                 try {
1337                     return connection.fetchNotifications(clientSequenceNumber,
1338                             maxNotifications,
1339                             timeout);
1340                 } catch (IOException ioe) {
1341                     org = ioe;
1342 
1343                     // inform of IOException
1344                     try {
1345                         communicatorAdmin.gotIOException(ioe);
1346 
1347                         // The connection should be re-established.
1348                         continue;
1349                     } catch (IOException ee) {
1350                         // No more fetch, the Exception will be re-thrown.
1351                         break;
1352                     } // never reached
1353                 } // never reached
1354             }
1355 
1356             // specially treating for an UnmarshalException
1357             if (org instanceof UnmarshalException) {
1358                 UnmarshalException ume = (UnmarshalException)org;
1359 
1360                 if (ume.detail instanceof ClassNotFoundException)
1361                     throw (ClassNotFoundException) ume.detail;
1362 
1363                 /* In Sun's RMI implementation, if a method return
1364                    contains an unserializable object, then we get
1365                    UnmarshalException wrapping WriteAbortedException
1366                    wrapping NotSerializableException.  In that case we
1367                    extract the NotSerializableException so that our
1368                    caller can realize it should try to skip past the
1369                    notification that presumably caused it.  It's not
1370                    certain that every other RMI implementation will
1371                    generate this exact exception sequence.  If not, we
1372                    will not detect that the problem is due to an
1373                    unserializable object, and we will stop trying to
1374                    receive notifications from the server.  It's not
1375                    clear we can do much better.  */
1376                 if (ume.detail instanceof WriteAbortedException) {
1377                     WriteAbortedException wae =
1378                             (WriteAbortedException) ume.detail;
1379                     if (wae.detail instanceof IOException)
1380                         throw (IOException) wae.detail;
1381                 }
1382             } else if (org instanceof MarshalException) {
1383                 // IIOP will throw MarshalException wrapping a NotSerializableException
1384                 // when a server fails to serialize a response.
1385                 MarshalException me = (MarshalException)org;
1386                 if (me.detail instanceof NotSerializableException) {
1387                     throw (NotSerializableException)me.detail;
1388                 }
1389             }
1390 
1391             // Not serialization problem, simply re-throw the orginal exception
1392             throw org;
1393         }
1394 
1395         protected Integer addListenerForMBeanRemovedNotif()
1396         throws IOException, InstanceNotFoundException {
1397             NotificationFilterSupport clientFilter =
1398                     new NotificationFilterSupport();
1399             clientFilter.enableType(
1400                     MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
1401             MarshalledObject<NotificationFilter> sFilter =
1402                 new MarshalledObject<NotificationFilter>(clientFilter);
1403 
1404             Integer[] listenerIDs;
1405             final ObjectName[] names =
1406                 new ObjectName[] {MBeanServerDelegate.DELEGATE_NAME};
1407             final MarshalledObject<NotificationFilter>[] filters =
1408                 Util.cast(new MarshalledObject<?>[] {sFilter});
1409             final Subject[] subjects = new Subject[] {null};
1410             try {
1411                 listenerIDs =
1412                         connection.addNotificationListeners(names,
1413                         filters,
1414                         subjects);
1415 
1416             } catch (IOException ioe) {
1417                 communicatorAdmin.gotIOException(ioe);
1418 
1419                 listenerIDs =
1420                         connection.addNotificationListeners(names,
1421                         filters,
1422                         subjects);
1423             }
1424             return listenerIDs[0];
1425         }
1426 
1427         protected void removeListenerForMBeanRemovedNotif(Integer id)
1428         throws IOException, InstanceNotFoundException,
1429                 ListenerNotFoundException {
1430             try {
1431                 connection.removeNotificationListeners(
1432                         MBeanServerDelegate.DELEGATE_NAME,
1433                         new Integer[] {id},
1434                         null);
1435             } catch (IOException ioe) {
1436                 communicatorAdmin.gotIOException(ioe);
1437 
1438                 connection.removeNotificationListeners(
1439                         MBeanServerDelegate.DELEGATE_NAME,
1440                         new Integer[] {id},
1441                         null);
1442             }
1443 
1444         }
1445 
1446         protected void lostNotifs(String message, long number) {
1447             final String notifType = JMXConnectionNotification.NOTIFS_LOST;
1448 
1449             final JMXConnectionNotification n =
1450                 new JMXConnectionNotification(notifType,
1451                                               RMIConnector.this,
1452                                               connectionId,
1453                                               clientNotifCounter++,
1454                                               message,
1455                                               Long.valueOf(number));
1456             sendNotification(n);
1457         }
1458     }
1459 
1460     private class RMIClientCommunicatorAdmin extends ClientCommunicatorAdmin {
1461         public RMIClientCommunicatorAdmin(long period) {
1462             super(period);
1463         }
1464 
1465         @Override
1466         public void gotIOException(IOException ioe) throws IOException {
1467             if (ioe instanceof NoSuchObjectException) {
1468                 // need to restart
1469                 super.gotIOException(ioe);
1470 
1471                 return;
1472             }
1473 
1474             // check if the connection is broken
1475             try {
1476                 connection.getDefaultDomain(null);
1477             } catch (IOException ioexc) {
1478                 boolean toClose = false;
1479 
1480                 synchronized(this) {
1481                     if (!terminated) {
1482                         terminated = true;
1483 
1484                         toClose = true;
1485                     }
1486                 }
1487 
1488                 if (toClose) {
1489                     // we should close the connection,
1490                     // but send a failed notif at first
1491                     final Notification failedNotif =
1492                             new JMXConnectionNotification(
1493                             JMXConnectionNotification.FAILED,
1494                             this,
1495                             connectionId,
1496                             clientNotifSeqNo++,
1497                             "Failed to communicate with the server: "+ioe.toString(),
1498                             ioe);
1499 
1500                     sendNotification(failedNotif);
1501 
1502                     try {
1503                         close(true);
1504                     } catch (Exception e) {
1505                         // OK.
1506                         // We are closing
1507                     }
1508                 }
1509             }
1510 
1511             // forward the exception
1512             if (ioe instanceof ServerException) {
1513                 /* Need to unwrap the exception.
1514                    Some user-thrown exception at server side will be wrapped by
1515                    rmi into a ServerException.
1516                    For example, a RMIConnnectorServer will wrap a
1517                    ClassNotFoundException into a UnmarshalException, and rmi
1518                    will throw a ServerException at client side which wraps this
1519                    UnmarshalException.
1520                    No failed notif here.
1521                  */
1522                 Throwable tt = ((ServerException)ioe).detail;
1523 
1524                 if (tt instanceof IOException) {
1525                     throw (IOException)tt;
1526                 } else if (tt instanceof RuntimeException) {
1527                     throw (RuntimeException)tt;
1528                 }
1529             }
1530 
1531             throw ioe;
1532         }
1533 
1534         public void reconnectNotificationListeners(ClientListenerInfo[] old) throws IOException {
1535             final int len  = old.length;
1536             int i;
1537 
1538             ClientListenerInfo[] clis = new ClientListenerInfo[len];
1539 
1540             final Subject[] subjects = new Subject[len];
1541             final ObjectName[] names = new ObjectName[len];
1542             final NotificationListener[] listeners = new NotificationListener[len];
1543             final NotificationFilter[] filters = new NotificationFilter[len];
1544             final MarshalledObject<NotificationFilter>[] mFilters =
1545                     Util.cast(new MarshalledObject<?>[len]);
1546             final Object[] handbacks = new Object[len];
1547 
1548             for (i=0;i<len;i++) {
1549                 subjects[i]  = old[i].getDelegationSubject();
1550                 names[i]     = old[i].getObjectName();
1551                 listeners[i] = old[i].getListener();
1552                 filters[i]   = old[i].getNotificationFilter();
1553                 mFilters[i]  = new MarshalledObject<NotificationFilter>(filters[i]);
1554                 handbacks[i] = old[i].getHandback();
1555             }
1556 
1557             try {
1558                 Integer[] ids = addListenersWithSubjects(names,mFilters,subjects,false);
1559 
1560                 for (i=0;i<len;i++) {
1561                     clis[i] = new ClientListenerInfo(ids[i],
1562                             names[i],
1563                             listeners[i],
1564                             filters[i],
1565                             handbacks[i],
1566                             subjects[i]);
1567                 }
1568 
1569                 rmiNotifClient.postReconnection(clis);
1570 
1571                 return;
1572             } catch (InstanceNotFoundException infe) {
1573                 // OK, we will do one by one
1574             }
1575 
1576             int j = 0;
1577             for (i=0;i<len;i++) {
1578                 try {
1579                     Integer id = addListenerWithSubject(names[i],
1580                             new MarshalledObject<NotificationFilter>(filters[i]),
1581                             subjects[i],
1582                             false);
1583 
1584                     clis[j++] = new ClientListenerInfo(id,
1585                             names[i],
1586                             listeners[i],
1587                             filters[i],
1588                             handbacks[i],
1589                             subjects[i]);
1590                 } catch (InstanceNotFoundException infe) {
1591                     logger.warning("reconnectNotificationListeners",
1592                             "Can't reconnect listener for " +
1593                             names[i]);
1594                 }
1595             }
1596 
1597             if (j != len) {
1598                 ClientListenerInfo[] tmp = clis;
1599                 clis = new ClientListenerInfo[j];
1600                 System.arraycopy(tmp, 0, clis, 0, j);
1601             }
1602 
1603             rmiNotifClient.postReconnection(clis);
1604         }
1605 
1606         protected void checkConnection() throws IOException {
1607             if (logger.debugOn())
1608                 logger.debug("RMIClientCommunicatorAdmin-checkConnection",
1609                         "Calling the method getDefaultDomain.");
1610 
1611             connection.getDefaultDomain(null);
1612         }
1613 
1614         protected void doStart() throws IOException {
1615             // Get RMIServer stub from directory or URL encoding if needed.
1616             RMIServer stub;
1617             try {
1618                 stub = (rmiServer!=null)?rmiServer:
1619                     findRMIServer(jmxServiceURL, env);
1620             } catch (NamingException ne) {
1621                 throw new IOException("Failed to get a RMI stub: "+ne);
1622             }
1623 
1624             // Connect IIOP Stub if needed.
1625             stub = connectStub(stub,env);
1626 
1627             // Calling newClient on the RMIServer stub.
1628             Object credentials = env.get(CREDENTIALS);
1629             connection = stub.newClient(credentials);
1630 
1631             // notif issues
1632             final ClientListenerInfo[] old = rmiNotifClient.preReconnection();
1633 
1634             reconnectNotificationListeners(old);
1635 
1636             connectionId = getConnectionId();
1637 
1638             Notification reconnectedNotif =
1639                     new JMXConnectionNotification(JMXConnectionNotification.OPENED,
1640                     this,
1641                     connectionId,
1642                     clientNotifSeqNo++,
1643                     "Reconnected to server",
1644                     null);
1645             sendNotification(reconnectedNotif);
1646 
1647         }
1648 
1649         protected void doStop() {
1650             try {
1651                 close();
1652             } catch (IOException ioe) {
1653                 logger.warning("RMIClientCommunicatorAdmin-doStop",
1654                         "Failed to call the method close():" + ioe);
1655                 logger.debug("RMIClientCommunicatorAdmin-doStop",ioe);
1656             }
1657         }
1658     }
1659 
1660     //--------------------------------------------------------------------
1661     // Private stuff - Serialization
1662     //--------------------------------------------------------------------
1663     /**
1664      * <p>In order to be usable, an IIOP stub must be connected to an ORB.
1665      * The stub is automatically connected to the ORB if:
1666      * <ul>
1667      *     <li> It was returned by the COS naming</li>
1668      *     <li> Its server counterpart has been registered in COS naming
1669      *          through JNDI.</li>
1670      * </ul>
1671      * Otherwise, it is not connected. A stub which is deserialized
1672      * from Jini is not connected. A stub which is obtained from a
1673      * non registered RMIIIOPServerImpl is not a connected.<br>
1674      * A stub which is not connected can't be serialized, and thus
1675      * can't be registered in Jini. A stub which is not connected can't
1676      * be used to invoke methods on the server.
1677      * <p>
1678      * In order to palliate this, this method will connect the
1679      * given stub if it is not yet connected. If the given
1680      * <var>RMIServer</var> is not an instance of
1681      * {@link javax.rmi.CORBA.Stub javax.rmi.CORBA.Stub}, then the
1682      * method do nothing and simply returns that stub. Otherwise,
1683      * this method will attempt to connect the stub to an ORB as
1684      * follows:
1685      * <ul>
1686      * <p>This method looks in the provided <var>environment</var> for
1687      * the "java.naming.corba.orb" property. If it is found, the
1688      * referenced object (an {@link org.omg.CORBA.ORB ORB}) is used to
1689      * connect the stub. Otherwise, a new org.omg.CORBA.ORB is created
1690      * by calling {@link
1691      * org.omg.CORBA.ORB#init(String[], Properties)
1692      * org.omg.CORBA.ORB.init((String[])null,(Properties)null)}
1693      * <p>The new created ORB is kept in a static
1694      * {@link WeakReference} and can be reused for connecting other
1695      * stubs. However, no reference is ever kept on the ORB provided
1696      * in the <var>environment</var> map, if any.
1697      * </ul>
1698      * @param rmiServer A RMI Server Stub.
1699      * @param environment An environment map, possibly containing an ORB.
1700      * @return the given stub.
1701      * @exception IllegalArgumentException if the
1702      *      <tt>java.naming.corba.orb</tt> property is specified and
1703      *      does not point to an {@link org.omg.CORBA.ORB ORB}.
1704      * @exception IOException if the connection to the ORB failed.
1705      **/
1706     static RMIServer connectStub(RMIServer rmiServer,
1707                                  Map<String, ?> environment)
1708         throws IOException {
1709         if (IIOPHelper.isStub(rmiServer)) {
1710             try {
1711                 IIOPHelper.getOrb(rmiServer);
1712             } catch (UnsupportedOperationException x) {
1713                 // BAD_OPERATION
1714                 IIOPHelper.connect(rmiServer, resolveOrb(environment));
1715             }
1716         }
1717         return rmiServer;
1718     }
1719 
1720     /**
1721      * Get the ORB specified by <var>environment</var>, or create a
1722      * new one.
1723      * <p>This method looks in the provided <var>environment</var> for
1724      * the "java.naming.corba.orb" property. If it is found, the
1725      * referenced object (an {@link org.omg.CORBA.ORB ORB}) is
1726      * returned. Otherwise, a new org.omg.CORBA.ORB is created
1727      * by calling {@link
1728      * org.omg.CORBA.ORB#init(String[], java.util.Properties)
1729      * org.omg.CORBA.ORB.init((String[])null,(Properties)null)}
1730      * <p>The new created ORB is kept in a static
1731      * {@link WeakReference} and can be reused for connecting other
1732      * stubs. However, no reference is ever kept on the ORB provided
1733      * in the <var>environment</var> map, if any.
1734      * @param environment An environment map, possibly containing an ORB.
1735      * @return An ORB.
1736      * @exception IllegalArgumentException if the
1737      *      <tt>java.naming.corba.orb</tt> property is specified and
1738      *      does not point to an {@link org.omg.CORBA.ORB ORB}.
1739      * @exception IOException if the ORB initialization failed.
1740      **/
1741     static Object resolveOrb(Map<String, ?> environment)
1742         throws IOException {
1743         if (environment != null) {
1744             final Object orb = environment.get(EnvHelp.DEFAULT_ORB);
1745             if (orb != null && !(IIOPHelper.isOrb(orb)))
1746                 throw new IllegalArgumentException(EnvHelp.DEFAULT_ORB +
1747                         " must be an instance of org.omg.CORBA.ORB.");
1748             if (orb != null) return orb;
1749         }
1750         final Object orb =
1751                 (RMIConnector.orb==null)?null:RMIConnector.orb.get();
1752         if (orb != null) return orb;
1753 
1754         final Object newOrb =
1755                 IIOPHelper.createOrb((String[])null, (Properties)null);
1756         RMIConnector.orb = new WeakReference<Object>(newOrb);
1757         return newOrb;
1758     }
1759 
1760     /**
1761      * Read RMIConnector fields from an {@link java.io.ObjectInputStream
1762      * ObjectInputStream}.
1763      * Calls <code>s.defaultReadObject()</code> and then initializes
1764      * all transient variables that need initializing.
1765      * @param s The ObjectInputStream to read from.
1766      * @exception InvalidObjectException if none of <var>rmiServer</var> stub
1767      *    or <var>jmxServiceURL</var> are set.
1768      * @see #RMIConnector(JMXServiceURL,Map)
1769      * @see #RMIConnector(RMIServer,Map)
1770      **/
1771     private void readObject(java.io.ObjectInputStream s)
1772     throws IOException, ClassNotFoundException  {
1773         s.defaultReadObject();
1774 
1775         if (rmiServer == null && jmxServiceURL == null) throw new
1776                 InvalidObjectException("rmiServer and jmxServiceURL both null");
1777 
1778         initTransients();
1779     }
1780 
1781     /**
1782      * Writes the RMIConnector fields to an {@link java.io.ObjectOutputStream
1783      * ObjectOutputStream}.
1784      * <p>Connects the underlying RMIServer stub to an ORB, if needed,
1785      * before serializing it. This is done using the environment
1786      * map that was provided to the constructor, if any, and as documented
1787      * in {@link javax.management.remote.rmi}.</p>
1788      * <p>This method then calls <code>s.defaultWriteObject()</code>.
1789      * Usually, <var>rmiServer</var> is null if this object
1790      * was constructed with a JMXServiceURL, and <var>jmxServiceURL</var>
1791      * is null if this object is constructed with a RMIServer stub.
1792      * <p>Note that the environment Map is not serialized, since the objects
1793      * it contains are assumed to be contextual and relevant only
1794      * with respect to the local environment (class loader, ORB, etc...).</p>
1795      * <p>After an RMIConnector is deserialized, it is assumed that the
1796      * user will call {@link #connect(Map)}, providing a new Map that
1797      * can contain values which are contextually relevant to the new
1798      * local environment.</p>
1799      * <p>Since connection to the ORB is needed prior to serializing, and
1800      * since the ORB to connect to is one of those contextual parameters,
1801      * it is not recommended to re-serialize a just de-serialized object -
1802      * as the de-serialized object has no map. Thus, when an RMIConnector
1803      * object is needed for serialization or transmission to a remote
1804      * application, it is recommended to obtain a new RMIConnector stub
1805      * by calling {@link RMIConnectorServer#toJMXConnector(Map)}.</p>
1806      * @param s The ObjectOutputStream to write to.
1807      * @exception InvalidObjectException if none of <var>rmiServer</var> stub
1808      *    or <var>jmxServiceURL</var> are set.
1809      * @see #RMIConnector(JMXServiceURL,Map)
1810      * @see #RMIConnector(RMIServer,Map)
1811      **/
1812     private void writeObject(java.io.ObjectOutputStream s)
1813     throws IOException {
1814         if (rmiServer == null && jmxServiceURL == null) throw new
1815                 InvalidObjectException("rmiServer and jmxServiceURL both null.");
1816         connectStub(this.rmiServer,env);
1817         s.defaultWriteObject();
1818     }
1819 
1820     // Initialization of transient variables.
1821     private void initTransients() {
1822         rmbscMap = new WeakHashMap<Subject, MBeanServerConnection>();
1823         connected = false;
1824         terminated = false;
1825 
1826         connectionBroadcaster = new NotificationBroadcasterSupport();
1827     }
1828 
1829     //--------------------------------------------------------------------
1830     // Private stuff - Check if stub can be trusted.
1831     //--------------------------------------------------------------------
1832 
1833     private static void checkStub(Remote stub,
1834             Class<?> stubClass) {
1835 
1836         // Check remote stub is from the expected class.
1837         //
1838         if (stub.getClass() != stubClass) {
1839             if (!Proxy.isProxyClass(stub.getClass())) {
1840                 throw new SecurityException(
1841                         "Expecting a " + stubClass.getName() + " stub!");
1842             } else {
1843                 InvocationHandler handler = Proxy.getInvocationHandler(stub);
1844                 if (handler.getClass() != RemoteObjectInvocationHandler.class)
1845                     throw new SecurityException(
1846                             "Expecting a dynamic proxy instance with a " +
1847                             RemoteObjectInvocationHandler.class.getName() +
1848                             " invocation handler!");
1849                 else
1850                     stub = (Remote) handler;
1851             }
1852         }
1853 
1854         // Check RemoteRef in stub is from the expected class
1855         // "sun.rmi.server.UnicastRef2".
1856         //
1857         RemoteRef ref = ((RemoteObject)stub).getRef();
1858         if (ref.getClass() != UnicastRef2.class)
1859             throw new SecurityException(
1860                     "Expecting a " + UnicastRef2.class.getName() +
1861                     " remote reference in stub!");
1862 
1863         // Check RMIClientSocketFactory in stub is from the expected class
1864         // "javax.rmi.ssl.SslRMIClientSocketFactory".
1865         //
1866         LiveRef liveRef = ((UnicastRef2)ref).getLiveRef();
1867         RMIClientSocketFactory csf = liveRef.getClientSocketFactory();
1868         if (csf == null || csf.getClass() != SslRMIClientSocketFactory.class)
1869             throw new SecurityException(
1870                     "Expecting a " + SslRMIClientSocketFactory.class.getName() +
1871                     " RMI client socket factory in stub!");
1872     }
1873 
1874     //--------------------------------------------------------------------
1875     // Private stuff - RMIServer creation
1876     //--------------------------------------------------------------------
1877 
1878     private RMIServer findRMIServer(JMXServiceURL directoryURL,
1879             Map<String, Object> environment)
1880             throws NamingException, IOException {
1881         final boolean isIiop = RMIConnectorServer.isIiopURL(directoryURL,true);
1882         if (isIiop) {
1883             // Make sure java.naming.corba.orb is in the Map.
1884             environment.put(EnvHelp.DEFAULT_ORB,resolveOrb(environment));
1885         }
1886 
1887         String path = directoryURL.getURLPath();
1888         int end = path.indexOf(';');
1889         if (end < 0) end = path.length();
1890         if (path.startsWith("/jndi/"))
1891             return findRMIServerJNDI(path.substring(6,end), environment, isIiop);
1892         else if (path.startsWith("/stub/"))
1893             return findRMIServerJRMP(path.substring(6,end), environment, isIiop);
1894         else if (path.startsWith("/ior/")) {
1895             if (!IIOPHelper.isAvailable())
1896                 throw new IOException("iiop protocol not available");
1897             return findRMIServerIIOP(path.substring(5,end), environment, isIiop);
1898         } else {
1899             final String msg = "URL path must begin with /jndi/ or /stub/ " +
1900                     "or /ior/: " + path;
1901             throw new MalformedURLException(msg);
1902         }
1903     }
1904 
1905     /**
1906      * Lookup the RMIServer stub in a directory.
1907      * @param jndiURL A JNDI URL indicating the location of the Stub
1908      *                (see {@link javax.management.remote.rmi}), e.g.:
1909      *   <ul><li><tt>rmi://registry-host:port/rmi-stub-name</tt></li>
1910      *       <li>or <tt>iiop://cosnaming-host:port/iiop-stub-name</tt></li>
1911      *       <li>or <tt>ldap://ldap-host:port/java-container-dn</tt></li>
1912      *   </ul>
1913      * @param env the environment Map passed to the connector.
1914      * @param isIiop true if the stub is expected to be an IIOP stub.
1915      * @return The retrieved RMIServer stub.
1916      * @exception NamingException if the stub couldn't be found.
1917      **/
1918     private RMIServer findRMIServerJNDI(String jndiURL, Map<String, ?> env,
1919             boolean isIiop)
1920             throws NamingException {
1921 
1922         InitialContext ctx = new InitialContext(EnvHelp.mapToHashtable(env));
1923 
1924         Object objref = ctx.lookup(jndiURL);
1925         ctx.close();
1926 
1927         if (isIiop)
1928             return narrowIIOPServer(objref);
1929         else
1930             return narrowJRMPServer(objref);
1931     }
1932 
1933     private static RMIServer narrowJRMPServer(Object objref) {
1934 
1935         return (RMIServer) objref;
1936     }
1937 
1938     private static RMIServer narrowIIOPServer(Object objref) {
1939         try {
1940             return IIOPHelper.narrow(objref, RMIServer.class);
1941         } catch (ClassCastException e) {
1942             if (logger.traceOn())
1943                 logger.trace("narrowIIOPServer","Failed to narrow objref=" +
1944                         objref + ": " + e);
1945             if (logger.debugOn()) logger.debug("narrowIIOPServer",e);
1946             return null;
1947         }
1948     }
1949 
1950     private RMIServer findRMIServerIIOP(String ior, Map<String, ?> env, boolean isIiop) {
1951         // could forbid "rmi:" URL here -- but do we need to?
1952         final Object orb = env.get(EnvHelp.DEFAULT_ORB);
1953         final Object stub = IIOPHelper.stringToObject(orb, ior);
1954         return IIOPHelper.narrow(stub, RMIServer.class);
1955     }
1956 
1957     private RMIServer findRMIServerJRMP(String base64, Map<String, ?> env, boolean isIiop)
1958         throws IOException {
1959         // could forbid "iiop:" URL here -- but do we need to?
1960         final byte[] serialized;
1961         try {
1962             serialized = base64ToByteArray(base64);
1963         } catch (IllegalArgumentException e) {
1964             throw new MalformedURLException("Bad BASE64 encoding: " +
1965                     e.getMessage());
1966         }
1967         final ByteArrayInputStream bin = new ByteArrayInputStream(serialized);
1968 
1969         final ClassLoader loader = EnvHelp.resolveClientClassLoader(env);
1970         final ObjectInputStream oin =
1971                 (loader == null) ?
1972                     new ObjectInputStream(bin) :
1973                     new ObjectInputStreamWithLoader(bin, loader);
1974         final Object stub;
1975         try {
1976             stub = oin.readObject();
1977         } catch (ClassNotFoundException e) {
1978             throw new MalformedURLException("Class not found: " + e);
1979         }
1980         return (RMIServer)stub;
1981     }
1982 
1983     private static final class ObjectInputStreamWithLoader
1984             extends ObjectInputStream {
1985         ObjectInputStreamWithLoader(InputStream in, ClassLoader cl)
1986         throws IOException {
1987             super(in);
1988             this.loader = cl;
1989         }
1990 
1991         @Override
1992         protected Class<?> resolveClass(ObjectStreamClass classDesc)
1993                 throws IOException, ClassNotFoundException {
1994             return Class.forName(classDesc.getName(), false, loader);
1995         }
1996 
1997         private final ClassLoader loader;
1998     }
1999 
2000     /*
2001        The following section of code avoids a class loading problem
2002        with RMI.  The problem is that an RMI stub, when deserializing
2003        a remote method return value or exception, will first of all
2004        consult the first non-bootstrap class loader it finds in the
2005        call stack.  This can lead to behavior that is not portable
2006        between implementations of the JMX Remote API.  Notably, an
2007        implementation on J2SE 1.4 will find the RMI stub's loader on
2008        the stack.  But in J2SE 5, this stub is loaded by the
2009        bootstrap loader, so RMI will find the loader of the user code
2010        that called an MBeanServerConnection method.
2011 
2012        To avoid this problem, we take advantage of what the RMI stub
2013        is doing internally.  Each remote call will end up calling
2014        ref.invoke(...), where ref is the RemoteRef parameter given to
2015        the RMI stub's constructor.  It is within this call that the
2016        deserialization will happen.  So we fabricate our own RemoteRef
2017        that delegates everything to the "real" one but that is loaded
2018        by a class loader that knows no other classes.  The class
2019        loader NoCallStackClassLoader does this: the RemoteRef is an
2020        instance of the class named by proxyRefClassName, which is
2021        fabricated by the class loader using byte code that is defined
2022        by the string below.
2023 
2024        The call stack when the deserialization happens is thus this:
2025        MBeanServerConnection.getAttribute (or whatever)
2026        -> RMIConnectionImpl_Stub.getAttribute
2027           -> ProxyRef.invoke(...getAttribute...)
2028              -> UnicastRef.invoke(...getAttribute...)
2029                 -> internal RMI stuff
2030 
2031        Here UnicastRef is the RemoteRef created when the stub was
2032        deserialized (which is of some RMI internal class).  It and the
2033        "internal RMI stuff" are loaded by the bootstrap loader, so are
2034        transparent to the stack search.  The first non-bootstrap
2035        loader found is our ProxyRefLoader, as required.
2036 
2037        In a future version of this code as integrated into J2SE 5,
2038        this workaround could be replaced by direct access to the
2039        internals of RMI.  For now, we use the same code base for J2SE
2040        and for the standalone Reference Implementation.
2041 
2042        The byte code below encodes the following class, compiled using
2043        J2SE 1.4.2 with the -g:none option.
2044 
2045         package com.sun.jmx.remote.internal;
2046 
2047         import java.lang.reflect.Method;
2048         import java.rmi.Remote;
2049         import java.rmi.server.RemoteRef;
2050         import com.sun.jmx.remote.internal.ProxyRef;
2051 
2052         public class PRef extends ProxyRef {
2053             public PRef(RemoteRef ref) {
2054                 super(ref);
2055             }
2056 
2057             public Object invoke(Remote obj, Method method,
2058                                  Object[] params, long opnum)
2059                     throws Exception {
2060                 return ref.invoke(obj, method, params, opnum);
2061             }
2062         }
2063      */
2064 
2065     private static final String rmiServerImplStubClassName =
2066         RMIServer.class.getName() + "Impl_Stub";
2067     private static final Class<?> rmiServerImplStubClass;
2068     private static final String rmiConnectionImplStubClassName =
2069             RMIConnection.class.getName() + "Impl_Stub";
2070     private static final Class<?> rmiConnectionImplStubClass;
2071     private static final String pRefClassName =
2072         "com.sun.jmx.remote.internal.PRef";
2073     private static final Constructor<?> proxyRefConstructor;
2074     static {
2075         final String pRefByteCodeString =
2076                 "\312\376\272\276\0\0\0.\0\27\12\0\5\0\15\11\0\4\0\16\13\0\17\0"+
2077                 "\20\7\0\21\7\0\22\1\0\6<init>\1\0\36(Ljava/rmi/server/RemoteRef;"+
2078                 ")V\1\0\4Code\1\0\6invoke\1\0S(Ljava/rmi/Remote;Ljava/lang/reflec"+
2079                 "t/Method;[Ljava/lang/Object;J)Ljava/lang/Object;\1\0\12Exception"+
2080                 "s\7\0\23\14\0\6\0\7\14\0\24\0\25\7\0\26\14\0\11\0\12\1\0\40com/"+
2081                 "sun/jmx/remote/internal/PRef\1\0$com/sun/jmx/remote/internal/Pr"+
2082                 "oxyRef\1\0\23java/lang/Exception\1\0\3ref\1\0\33Ljava/rmi/serve"+
2083                 "r/RemoteRef;\1\0\31java/rmi/server/RemoteRef\0!\0\4\0\5\0\0\0\0"+
2084                 "\0\2\0\1\0\6\0\7\0\1\0\10\0\0\0\22\0\2\0\2\0\0\0\6*+\267\0\1\261"+
2085                 "\0\0\0\0\0\1\0\11\0\12\0\2\0\10\0\0\0\33\0\6\0\6\0\0\0\17*\264\0"+
2086                 "\2+,-\26\4\271\0\3\6\0\260\0\0\0\0\0\13\0\0\0\4\0\1\0\14\0\0";
2087         final byte[] pRefByteCode =
2088                 NoCallStackClassLoader.stringToBytes(pRefByteCodeString);
2089         PrivilegedExceptionAction<Constructor<?>> action =
2090                 new PrivilegedExceptionAction<Constructor<?>>() {
2091             public Constructor<?> run() throws Exception {
2092                 Class thisClass = RMIConnector.class;
2093                 ClassLoader thisLoader = thisClass.getClassLoader();
2094                 ProtectionDomain thisProtectionDomain =
2095                         thisClass.getProtectionDomain();
2096                 String[] otherClassNames = {ProxyRef.class.getName()};
2097                 ClassLoader cl =
2098                         new NoCallStackClassLoader(pRefClassName,
2099                         pRefByteCode,
2100                         otherClassNames,
2101                         thisLoader,
2102                         thisProtectionDomain);
2103                 Class<?> c = cl.loadClass(pRefClassName);
2104                 return c.getConstructor(RemoteRef.class);
2105             }
2106         };
2107 
2108         Class<?> serverStubClass;
2109         try {
2110             serverStubClass = Class.forName(rmiServerImplStubClassName);
2111         } catch (Exception e) {
2112             logger.error("<clinit>",
2113                     "Failed to instantiate " +
2114                     rmiServerImplStubClassName + ": " + e);
2115             logger.debug("<clinit>",e);
2116             serverStubClass = null;
2117         }
2118         rmiServerImplStubClass = serverStubClass;
2119 
2120         Class<?> stubClass;
2121         Constructor<?> constr;
2122         try {
2123             stubClass = Class.forName(rmiConnectionImplStubClassName);
2124             constr = (Constructor<?>) AccessController.doPrivileged(action);
2125         } catch (Exception e) {
2126             logger.error("<clinit>",
2127                     "Failed to initialize proxy reference constructor "+
2128                     "for " + rmiConnectionImplStubClassName + ": " + e);
2129             logger.debug("<clinit>",e);
2130             stubClass = null;
2131             constr = null;
2132         }
2133         rmiConnectionImplStubClass = stubClass;
2134         proxyRefConstructor = constr;
2135     }
2136 
2137     private static RMIConnection shadowJrmpStub(RemoteObject stub)
2138     throws InstantiationException, IllegalAccessException,
2139             InvocationTargetException, ClassNotFoundException,
2140             NoSuchMethodException {
2141         RemoteRef ref = stub.getRef();
2142         RemoteRef proxyRef = (RemoteRef)
2143             proxyRefConstructor.newInstance(new Object[] {ref});
2144         final Constructor<?> rmiConnectionImplStubConstructor =
2145             rmiConnectionImplStubClass.getConstructor(RemoteRef.class);
2146         Object[] args = {proxyRef};
2147         RMIConnection proxyStub = (RMIConnection)
2148         rmiConnectionImplStubConstructor.newInstance(args);
2149         return proxyStub;
2150     }
2151 
2152     /*
2153        The following code performs a similar trick for RMI/IIOP to the
2154        one described above for RMI/JRMP.  Unlike JRMP, though, we
2155        can't easily insert an object between the RMIConnection stub
2156        and the RMI/IIOP deserialization code, as explained below.
2157 
2158        A method in an RMI/IIOP stub does the following.  It makes an
2159        org.omg.CORBA_2_3.portable.OutputStream for each request, and
2160        writes the parameters to it.  Then it calls
2161        _invoke(OutputStream) which it inherits from CORBA's
2162        ObjectImpl.  That returns an
2163        org.omg.CORBA_2_3.portable.InputStream.  The return value is
2164        read from this InputStream.  So the stack during
2165        deserialization looks like this:
2166 
2167        MBeanServerConnection.getAttribute (or whatever)
2168        -> _RMIConnection_Stub.getAttribute
2169           -> Util.readAny (a CORBA method)
2170              -> InputStream.read_any
2171                 -> internal CORBA stuff
2172 
2173        What we would have *liked* to have done would be the same thing
2174        as for RMI/JRMP.  We create a "ProxyDelegate" that is an
2175        org.omg.CORBA.portable.Delegate that simply forwards every
2176        operation to the real original Delegate from the RMIConnection
2177        stub, except that the InputStream returned by _invoke is
2178        wrapped by a "ProxyInputStream" that is loaded by our
2179        NoCallStackClassLoader.
2180 
2181        Unfortunately, this doesn't work, at least with Sun's J2SE
2182        1.4.2, because the CORBA code is not designed to allow you to
2183        change Delegates arbitrarily.  You get a ClassCastException
2184        from code that expects the Delegate to implement an internal
2185        interface.
2186 
2187        So instead we do the following.  We create a subclass of the
2188        stub that overrides the _invoke method so as to wrap the
2189        returned InputStream in a ProxyInputStream.  We create a
2190        subclass of ProxyInputStream using the NoCallStackClassLoader
2191        and override its read_any and read_value(Class) methods.
2192        (These are the only methods called during deserialization of
2193        MBeanServerConnection return values.)  We extract the Delegate
2194        from the original stub and insert it into our subclass stub,
2195        and away we go.  The state of a stub consists solely of its
2196        Delegate.
2197 
2198        We also need to catch ApplicationException, which will encode
2199        any exceptions declared in the throws clause of the called
2200        method.  Its InputStream needs to be wrapped in a
2201        ProxyInputSteam too.
2202 
2203        We override _releaseReply in the stub subclass so that it
2204        replaces a ProxyInputStream argument with the original
2205        InputStream.  This avoids problems if the implementation of
2206        _releaseReply ends up casting this InputStream to an
2207        implementation-specific interface (which in Sun's J2SE 5 it
2208        does).
2209 
2210        It is not strictly necessary for the stub subclass to be loaded
2211        by a NoCallStackClassLoader, since the call-stack search stops
2212        at the ProxyInputStream subclass.  However, it is convenient
2213        for two reasons.  One is that it means that the
2214        ProxyInputStream subclass can be accessed directly, without
2215        using reflection.  The other is that it avoids build problems,
2216        since usually stubs are created after other classes are
2217        compiled, so we can't access them from this class without,
2218        again, using reflection.
2219 
2220        The strings below encode the following two Java classes,
2221        compiled using javac -g:none.
2222 
2223         package com.sun.jmx.remote.protocol.iiop;
2224 
2225         import org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub;
2226 
2227         import org.omg.CORBA.portable.ApplicationException;
2228         import org.omg.CORBA.portable.InputStream;
2229         import org.omg.CORBA.portable.OutputStream;
2230         import org.omg.CORBA.portable.RemarshalException;
2231 
2232         public class ProxyStub extends _RMIConnection_Stub {
2233             public InputStream _invoke(OutputStream out)
2234                     throws ApplicationException, RemarshalException {
2235                 try {
2236                     return new PInputStream(super._invoke(out));
2237                 } catch (ApplicationException e) {
2238                     InputStream pis = new PInputStream(e.getInputStream());
2239                     throw new ApplicationException(e.getId(), pis);
2240                 }
2241             }
2242 
2243             public void _releaseReply(InputStream in) {
2244                 if (in != null)
2245                     in = ((PInputStream)in).getProxiedInputStream();
2246                 super._releaseReply(in);
2247             }
2248         }
2249 
2250         package com.sun.jmx.remote.protocol.iiop;
2251 
2252         public class PInputStream extends ProxyInputStream {
2253             public PInputStream(org.omg.CORBA.portable.InputStream in) {
2254                 super(in);
2255             }
2256 
2257             public org.omg.CORBA.Any read_any() {
2258                 return in.read_any();
2259             }
2260 
2261             public java.io.Serializable read_value(Class clz) {
2262                 return narrow().read_value(clz);
2263             }
2264         }
2265 
2266 
2267      */
2268     private static final String iiopConnectionStubClassName =
2269         "org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub";
2270     private static final String proxyStubClassName =
2271         "com.sun.jmx.remote.protocol.iiop.ProxyStub";
2272     private static final String ProxyInputStreamClassName =
2273         "com.sun.jmx.remote.protocol.iiop.ProxyInputStream";
2274     private static final String pInputStreamClassName =
2275         "com.sun.jmx.remote.protocol.iiop.PInputStream";
2276     private static final Class<?> proxyStubClass;
2277     static {
2278         final String proxyStubByteCodeString =
2279                 "\312\376\272\276\0\0\0\63\0+\12\0\14\0\30\7\0\31\12\0\14\0\32\12"+
2280                 "\0\2\0\33\7\0\34\12\0\5\0\35\12\0\5\0\36\12\0\5\0\37\12\0\2\0 "+
2281                 "\12\0\14\0!\7\0\"\7\0#\1\0\6<init>\1\0\3()V\1\0\4Code\1\0\7_in"+
2282                 "voke\1\0K(Lorg/omg/CORBA/portable/OutputStream;)Lorg/omg/CORBA"+
2283                 "/portable/InputStream;\1\0\15StackMapTable\7\0\34\1\0\12Except"+
2284                 "ions\7\0$\1\0\15_releaseReply\1\0'(Lorg/omg/CORBA/portable/Inp"+
2285                 "utStream;)V\14\0\15\0\16\1\0-com/sun/jmx/remote/protocol/iiop/"+
2286                 "PInputStream\14\0\20\0\21\14\0\15\0\27\1\0+org/omg/CORBA/porta"+
2287                 "ble/ApplicationException\14\0%\0&\14\0'\0(\14\0\15\0)\14\0*\0&"+
2288                 "\14\0\26\0\27\1\0*com/sun/jmx/remote/protocol/iiop/ProxyStub\1"+
2289                 "\0<org/omg/stub/javax/management/remote/rmi/_RMIConnection_Stu"+
2290                 "b\1\0)org/omg/CORBA/portable/RemarshalException\1\0\16getInput"+
2291                 "Stream\1\0&()Lorg/omg/CORBA/portable/InputStream;\1\0\5getId\1"+
2292                 "\0\24()Ljava/lang/String;\1\09(Ljava/lang/String;Lorg/omg/CORB"+
2293                 "A/portable/InputStream;)V\1\0\25getProxiedInputStream\0!\0\13\0"+
2294                 "\14\0\0\0\0\0\3\0\1\0\15\0\16\0\1\0\17\0\0\0\21\0\1\0\1\0\0\0\5"+
2295                 "*\267\0\1\261\0\0\0\0\0\1\0\20\0\21\0\2\0\17\0\0\0G\0\4\0\4\0\0"+
2296                 "\0'\273\0\2Y*+\267\0\3\267\0\4\260M\273\0\2Y,\266\0\6\267\0\4N"+
2297                 "\273\0\5Y,\266\0\7-\267\0\10\277\0\1\0\0\0\14\0\15\0\5\0\1\0\22"+
2298                 "\0\0\0\6\0\1M\7\0\23\0\24\0\0\0\6\0\2\0\5\0\25\0\1\0\26\0\27\0"+
2299                 "\1\0\17\0\0\0'\0\2\0\2\0\0\0\22+\306\0\13+\300\0\2\266\0\11L*+"+
2300                 "\267\0\12\261\0\0\0\1\0\22\0\0\0\3\0\1\14\0\0";
2301         final String pInputStreamByteCodeString =
2302                 "\312\376\272\276\0\0\0\63\0\36\12\0\7\0\17\11\0\6\0\20\12\0\21"+
2303                 "\0\22\12\0\6\0\23\12\0\24\0\25\7\0\26\7\0\27\1\0\6<init>\1\0'("+
2304                 "Lorg/omg/CORBA/portable/InputStream;)V\1\0\4Code\1\0\10read_an"+
2305                 "y\1\0\25()Lorg/omg/CORBA/Any;\1\0\12read_value\1\0)(Ljava/lang"+
2306                 "/Class;)Ljava/io/Serializable;\14\0\10\0\11\14\0\30\0\31\7\0\32"+
2307                 "\14\0\13\0\14\14\0\33\0\34\7\0\35\14\0\15\0\16\1\0-com/sun/jmx"+
2308                 "/remote/protocol/iiop/PInputStream\1\0\61com/sun/jmx/remote/pr"+
2309                 "otocol/iiop/ProxyInputStream\1\0\2in\1\0$Lorg/omg/CORBA/portab"+
2310                 "le/InputStream;\1\0\"org/omg/CORBA/portable/InputStream\1\0\6n"+
2311                 "arrow\1\0*()Lorg/omg/CORBA_2_3/portable/InputStream;\1\0&org/o"+
2312                 "mg/CORBA_2_3/portable/InputStream\0!\0\6\0\7\0\0\0\0\0\3\0\1\0"+
2313                 "\10\0\11\0\1\0\12\0\0\0\22\0\2\0\2\0\0\0\6*+\267\0\1\261\0\0\0"+
2314                 "\0\0\1\0\13\0\14\0\1\0\12\0\0\0\24\0\1\0\1\0\0\0\10*\264\0\2\266"+
2315                 "\0\3\260\0\0\0\0\0\1\0\15\0\16\0\1\0\12\0\0\0\25\0\2\0\2\0\0\0"+
2316                 "\11*\266\0\4+\266\0\5\260\0\0\0\0\0\0";
2317         final byte[] proxyStubByteCode =
2318                 NoCallStackClassLoader.stringToBytes(proxyStubByteCodeString);
2319         final byte[] pInputStreamByteCode =
2320                 NoCallStackClassLoader.stringToBytes(pInputStreamByteCodeString);
2321         final String[] classNames={proxyStubClassName, pInputStreamClassName};
2322         final byte[][] byteCodes = {proxyStubByteCode, pInputStreamByteCode};
2323         final String[] otherClassNames = {
2324             iiopConnectionStubClassName,
2325             ProxyInputStreamClassName,
2326         };
2327         if (IIOPHelper.isAvailable()) {
2328             PrivilegedExceptionAction<Class<?>> action =
2329                 new PrivilegedExceptionAction<Class<?>>() {
2330               public Class<?> run() throws Exception {
2331                 Class thisClass = RMIConnector.class;
2332                 ClassLoader thisLoader = thisClass.getClassLoader();
2333                 ProtectionDomain thisProtectionDomain =
2334                         thisClass.getProtectionDomain();
2335                 ClassLoader cl =
2336                         new NoCallStackClassLoader(classNames,
2337                         byteCodes,
2338                         otherClassNames,
2339                         thisLoader,
2340                         thisProtectionDomain);
2341                 return cl.loadClass(proxyStubClassName);
2342               }
2343             };
2344             Class<?> stubClass;
2345             try {
2346                 stubClass = AccessController.doPrivileged(action);
2347             } catch (Exception e) {
2348                 logger.error("<clinit>",
2349                         "Unexpected exception making shadow IIOP stub class: "+e);
2350                 logger.debug("<clinit>",e);
2351                 stubClass = null;
2352             }
2353             proxyStubClass = stubClass;
2354         } else {
2355             proxyStubClass = null;
2356         }
2357     }
2358 
2359     private static RMIConnection shadowIiopStub(Object stub)
2360     throws InstantiationException, IllegalAccessException {
2361         Object proxyStub = proxyStubClass.newInstance();
2362         IIOPHelper.setDelegate(proxyStub, IIOPHelper.getDelegate(stub));
2363         return (RMIConnection) proxyStub;
2364     }
2365 
2366     private static RMIConnection getConnection(RMIServer server,
2367             Object credentials,
2368             boolean checkStub)
2369             throws IOException {
2370         RMIConnection c = server.newClient(credentials);
2371         if (checkStub) checkStub(c, rmiConnectionImplStubClass);
2372         try {
2373             if (c.getClass() == rmiConnectionImplStubClass)
2374                 return shadowJrmpStub((RemoteObject) c);
2375             if (c.getClass().getName().equals(iiopConnectionStubClassName))
2376                 return shadowIiopStub(c);
2377             logger.trace("getConnection",
2378                     "Did not wrap " + c.getClass() + " to foil " +
2379                     "stack search for classes: class loading semantics " +
2380                     "may be incorrect");
2381         } catch (Exception e) {
2382             logger.error("getConnection",
2383                     "Could not wrap " + c.getClass() + " to foil " +
2384                     "stack search for classes: class loading semantics " +
2385                     "may be incorrect: " + e);
2386             logger.debug("getConnection",e);
2387             // so just return the original stub, which will work for all
2388             // but the most exotic class loading situations
2389         }
2390         return c;
2391     }
2392 
2393     private static byte[] base64ToByteArray(String s) {
2394         int sLen = s.length();
2395         int numGroups = sLen/4;
2396         if (4*numGroups != sLen)
2397             throw new IllegalArgumentException(
2398                     "String length must be a multiple of four.");
2399         int missingBytesInLastGroup = 0;
2400         int numFullGroups = numGroups;
2401         if (sLen != 0) {
2402             if (s.charAt(sLen-1) == '=') {
2403                 missingBytesInLastGroup++;
2404                 numFullGroups--;
2405             }
2406             if (s.charAt(sLen-2) == '=')
2407                 missingBytesInLastGroup++;
2408         }
2409         byte[] result = new byte[3*numGroups - missingBytesInLastGroup];
2410 
2411         // Translate all full groups from base64 to byte array elements
2412         int inCursor = 0, outCursor = 0;
2413         for (int i=0; i<numFullGroups; i++) {
2414             int ch0 = base64toInt(s.charAt(inCursor++));
2415             int ch1 = base64toInt(s.charAt(inCursor++));
2416             int ch2 = base64toInt(s.charAt(inCursor++));
2417             int ch3 = base64toInt(s.charAt(inCursor++));
2418             result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
2419             result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
2420             result[outCursor++] = (byte) ((ch2 << 6) | ch3);
2421         }
2422 
2423         // Translate partial group, if present
2424         if (missingBytesInLastGroup != 0) {
2425             int ch0 = base64toInt(s.charAt(inCursor++));
2426             int ch1 = base64toInt(s.charAt(inCursor++));
2427             result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
2428 
2429             if (missingBytesInLastGroup == 1) {
2430                 int ch2 = base64toInt(s.charAt(inCursor++));
2431                 result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
2432             }
2433         }
2434         // assert inCursor == s.length()-missingBytesInLastGroup;
2435         // assert outCursor == result.length;
2436         return result;
2437     }
2438 
2439     /**
2440      * Translates the specified character, which is assumed to be in the
2441      * "Base 64 Alphabet" into its equivalent 6-bit positive integer.
2442      *
2443      * @throws IllegalArgumentException if
2444      *        c is not in the Base64 Alphabet.
2445      */
2446     private static int base64toInt(char c) {
2447         int result;
2448 
2449         if (c >= base64ToInt.length)
2450             result = -1;
2451         else
2452             result = base64ToInt[c];
2453 
2454         if (result < 0)
2455             throw new IllegalArgumentException("Illegal character " + c);
2456         return result;
2457     }
2458 
2459     /**
2460      * This array is a lookup table that translates unicode characters
2461      * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
2462      * into their 6-bit positive integer equivalents.  Characters that
2463      * are not in the Base64 alphabet but fall within the bounds of the
2464      * array are translated to -1.
2465      */
2466     private static final byte base64ToInt[] = {
2467         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2468         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2469         -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
2470         55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
2471         5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
2472         24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
2473         35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
2474     };
2475 
2476     //--------------------------------------------------------------------
2477     // Private stuff - Find / Set default class loader
2478     //--------------------------------------------------------------------
2479     private ClassLoader pushDefaultClassLoader() {
2480         final Thread t = Thread.currentThread();
2481         final ClassLoader old =  t.getContextClassLoader();
2482         if (defaultClassLoader != null)
2483             AccessController.doPrivileged(new PrivilegedAction<Void>() {
2484                 public Void run() {
2485                     t.setContextClassLoader(defaultClassLoader);
2486                     return null;
2487                 }
2488             });
2489             return old;
2490     }
2491 
2492     private void popDefaultClassLoader(final ClassLoader old) {
2493         AccessController.doPrivileged(new PrivilegedAction<Void>() {
2494             public Void run() {
2495                 Thread.currentThread().setContextClassLoader(old);
2496                 return null;
2497             }
2498         });
2499     }
2500 
2501     //--------------------------------------------------------------------
2502     // Private variables
2503     //--------------------------------------------------------------------
2504     /**
2505      * @serial The RMIServer stub of the RMI JMX Connector server to
2506      * which this client connector is (or will be) connected. This
2507      * field can be null when <var>jmxServiceURL</var> is not
2508      * null. This includes the case where <var>jmxServiceURL</var>
2509      * contains a serialized RMIServer stub. If both
2510      * <var>rmiServer</var> and <var>jmxServiceURL</var> are null then
2511      * serialization will fail.
2512      *
2513      * @see #RMIConnector(RMIServer,Map)
2514      **/
2515     private final RMIServer rmiServer;
2516 
2517     /**
2518      * @serial The JMXServiceURL of the RMI JMX Connector server to
2519      * which this client connector will be connected. This field can
2520      * be null when <var>rmiServer</var> is not null. If both
2521      * <var>rmiServer</var> and <var>jmxServiceURL</var> are null then
2522      * serialization will fail.
2523      *
2524      * @see #RMIConnector(JMXServiceURL,Map)
2525      **/
2526     private final JMXServiceURL jmxServiceURL;
2527 
2528     // ---------------------------------------------------------
2529     // WARNING - WARNING - WARNING - WARNING - WARNING - WARNING
2530     // ---------------------------------------------------------
2531     // Any transient variable which needs to be initialized should
2532     // be initialized in the method initTransient()
2533     private transient Map<String, Object> env;
2534     private transient ClassLoader defaultClassLoader;
2535     private transient RMIConnection connection;
2536     private transient String connectionId;
2537 
2538     private transient long clientNotifSeqNo = 0;
2539 
2540     private transient WeakHashMap<Subject, MBeanServerConnection> rmbscMap;
2541 
2542     private transient RMINotifClient rmiNotifClient;
2543     // = new RMINotifClient(new Integer(0));
2544 
2545     private transient long clientNotifCounter = 0;
2546 
2547     private transient boolean connected;
2548     // = false;
2549     private transient boolean terminated;
2550     // = false;
2551 
2552     private transient Exception closeException;
2553 
2554     private transient NotificationBroadcasterSupport connectionBroadcaster;
2555 
2556     private transient ClientCommunicatorAdmin communicatorAdmin;
2557 
2558     /**
2559      * A static WeakReference to an {@link org.omg.CORBA.ORB ORB} to
2560      * connect unconnected stubs.
2561      **/
2562     private static volatile WeakReference<Object> orb = null;
2563 
2564     // TRACES & DEBUG
2565     //---------------
2566     private static String objects(final Object[] objs) {
2567         if (objs == null)
2568             return "null";
2569         else
2570             return Arrays.asList(objs).toString();
2571     }
2572 
2573     private static String strings(final String[] strs) {
2574         return objects(strs);
2575     }
2576 }